﻿using DoNetDrive.AspWebSocke.WebSocketClient;
using DoNetDrive.Core;
using DoNetDrive.Core.Command;
using DoNetDrive.Core.Connector;
using DoNetDrive.Core.Data;
using Face8280WebSocketServer.Command;
using Face8280WebSocketServer.Command.Person;
using Face8280WebSocketServer.Command.PushMessage;
using Face8280WebSocketServer.Command.Remote;
using Face8280WebSocketServer.Controllers.DeviceAPI;
using Face8280WebSocketServer.DTO.FaceDevice;
using Face8280WebSocketServer.DTO.FaceRecard;
using Face8280WebSocketServer.DTO.PushMessage;
using Face8280WebSocketServer.Interface;
using Face8280WebSocketServer.Utility;
using FaceFace8280WebSocketServer.Option;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Serialization;
using System.Collections.Concurrent;
using System.Text;

namespace Face8280WebSocketServer
{
    public class MyBackgroundService : BackgroundService
    {
        private IFaceDriveServiceNew _DeviceDB;
        private IFaceRecordService _FaceRecordService;
        private IDeviceAccessService AccessService;
        private ICacheService _CacheService;
        IOptionsMonitor<PeopleOption> _OptionsMonitor;
        IFaceIOLogService _FaceIOLogService;
        public static string HostUrl;
        public static ConcurrentDictionary<string, FaceDeviceStatus> DeviceList = new ConcurrentDictionary<string, FaceDeviceStatus>();
        public MyBackgroundService(IFaceDriveServiceNew driveserver,
            IFaceRecordService faceRecord, IDeviceAccessService accessService,
            ICacheService cacheService,
            IOptionsMonitor<PeopleOption> optionsMonitor,
             IFaceIOLogService faceIOLogService)
        {
            _DeviceDB = driveserver;
            _FaceRecordService = faceRecord;
            _CacheService = cacheService;
            AccessService = accessService;
            _OptionsMonitor = optionsMonitor;
            _FaceIOLogService = faceIOLogService;
        }


        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            _CacheService.LoadCache();
            Allocator.SetLogService(_FaceIOLogService);
            CheckConnect();
            HandlePushMessage();
            BackgroundService();
            return Task.CompletedTask;
        }
        /// <summary>
        /// 检查设备连接
        /// </summary>
        private void CheckConnect()
        {
            var list = _CacheService.GetDevices();
            foreach (var item in list)
            {
                if (item.ConnectType == 0)
                {
                    if (!string.IsNullOrWhiteSpace(item.DeviceIP))
                    {
                        Allocator.WebSocketClients.TryAdd(item.DeviceSn, Allocator.GetWebSocketClientDetail(item.DeviceSn, item.DeviceIP, item.DevicePort, item.Token));
                    }

                }
            }
            Task.Factory.StartNew(async () =>
            {
                while (true)
                {
                    await Task.Delay(15000);
                    try
                    {
                        foreach (var item in Allocator.WebSocketClients.Values)
                        {
                            Allocator.OpenConnector(item);
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex);
                    }

                }
            });
        }
        /// <summary>
        /// 后台任务
        /// </summary>
        private void BackgroundService()
        {
            Task.Factory.StartNew(async () =>
           {
               while (true)
               {

                   var deviceList = _CacheService.GetDevices();
                   foreach (var item in deviceList)
                   {
                       var conDel = await Allocator.GetConnectorDetail(item.DeviceSn);
                       if (conDel is WebSocketClientDetail clientDetail)
                       {
                           if (!clientDetail.Connected) continue;
                       }
                       var comDel = Allocator.GetCommandDetail(conDel);
                       comDel.Timeout = 10000;
                       if (item.AddPeople.Count > 0)
                       {
                           List<object> RetryList = new List<object>();
                           while (item.AddPeople.Count > 0)
                           {
                               var peopleDto = item.AddPeople.Dequeue() as Person_DTO;
                               try
                               {
                                   var photo = _CacheService.GetPeopleImage(peopleDto.Code);
                                   if (photo != null)
                                   {
                                       peopleDto.FeatureData = new List<Command.Models.FeatureDataModel>();
                                       Command.Models.FeatureDataModel feature;
                                       if (_OptionsMonitor.CurrentValue.UseUrl)
                                       {
                                           var url = HostUrl + "/" + photo.PhotoUrl;
                                           feature = new Command.Models.FeatureDataModel(Command.Models.FeatureType.Photo, url, photo.Size);
                                           feature.Md5 = MD5Utility.Encrypt(photo.Photo);
                                       }
                                       else
                                       {
                                           feature = new Command.Models.FeatureDataModel(Command.Models.FeatureType.Photo, photo.Photo);
                                       }
                                       peopleDto.FeatureData.Add(feature);
                                   }
                                   AddPerson cmd = new AddPerson(comDel, peopleDto);
                                   await Allocator.AddCommandAsync(cmd);
                                   if (cmd.getResult() is AddPersonResult result)
                                   {
                                       // int error = int.Parse(result.ResultObj?.Error ?? "0");
                                       // if(int.TryParse(result.ResultObj?.Error,out var error))
                                       //   Console.WriteLine(cmd.FaceIOLog.RequestBody);
                                       // Console.WriteLine(result.JsonValue);

                                       AccessService.SavePeopleAccessUploadResult(item.DeviceSn, peopleDto.Code, result.ResultObj.RepeatCode, result.ResultObj.Result);
                                   }
                                   else
                                   {
                                       RetryList.Add(peopleDto);
                                   }
                               }
                               catch (Exception ex)
                               {
                                   Console.WriteLine(ex);
                                   RetryList.Add(peopleDto);
                               }
                           }
                           if (RetryList.Count > 0)
                           {
                               RetryList.ForEach(a => item.AddPeople.Enqueue(a));
                           }
                       }
                       if (item.DeletePeople.Count > 0 || item.DeleteAllPeople)
                       {
                           try
                           {
                               byte deleteAll = 0;
                               if (item.DeleteAllPeople == true)
                               {
                                   deleteAll = 1;
                               }
                               var peoples = item.DeletePeople.ToList();
                               DeletePerson cmd = new(comDel, peoples, deleteAll);
                               await Allocator.AddCommandAsync(cmd);
                               if (cmd.getResult() is CommandResult result && result.ResultObj.Result == 200)
                               {
                                   if (peoples.Count > 0)
                                       AccessService.Delete(item.DeviceSn, peoples);
                                   item.DeletePeople.Clear();
                                   item.DeleteAllPeople = false;
                               }
                           }
                           catch (Exception ex)
                           {
                               item.DeletePeople.Clear();
                               item.DeleteAllPeople = false;
                           }
                       }
                   }

                   await Task.Delay(1000);
               }
           });
        }
        /// <summary>
        /// 处理推送消息
        /// </summary>
        private void HandlePushMessage()
        {
            Task.Factory.StartNew(async () =>
          {
              while (true)
              {
                  try
                  {
                      if (Allocator.PushMessagesBag.TryTake(out var message))
                      {
                          var faceDeviceStatus = _CacheService.GetDevice(message.DeviceSn);
                          if (faceDeviceStatus != null)
                          {
                              var messageType = message.Message.Property("msgtype", StringComparison.CurrentCultureIgnoreCase).Value.ToString();
                              switch (messageType.ToLower())
                              {
                                  case "pushcardrecord":
                                      await CardRecord(message, faceDeviceStatus);
                                      break;
                                  case "pushstatus":
                                      await HandlePushStatus(message, faceDeviceStatus);
                                      break;
                                  default:
                                      break;
                              }
                          }
                      }
                  }
                  catch (Exception ex)
                  {
                      Console.WriteLine(ex);
                  }
                  await Task.Delay(300);
              }
          });
        }
        /// <summary>
        /// 处理心跳
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        private async Task HandlePushStatus(PushMessageInfo info, FaceDeviceStatus device)
        {
            // var device = _CacheService.GetDevice(info.DeviceSn);
            if (device == null)
                return;
            var push = info.Message.ToString().FromJson<PushWorkStatus>();
            var status = push.workstatus.ToString().FromJson<Command.Models.WorkStatus>();
            device.Ups = status.Ups;
            device.Voltage = status.Voltage;
            device.Temperature = status.Temperature;
            device.Relay = status.Relay;
            device.Keepopen = status.Keepopen;
            device.DoorSensor = status.DoorSensor;
            device.LockSensor = status.LockSensor;
            device.Locked = status.Locked;
            device.MsgPush = status.MsgPush;
            device.Alarm = status.Alarm;
            device.Relays = status.Relays;
            device.OnlineTime = DateTime.Now;
            device.LastKeepaliveTime = DateTime.Now;
            var comDel = await Allocator.GetCommandDetail(info.DeviceSn);
            var cmd = new PushStatus(comDel);
            Allocator.AddCommand(cmd);
        }

        private async Task CardRecord(PushMessageInfo info, FaceDeviceStatus faceDeviceStatus)
        {
            var record = info.Message.ToString().FromJson<DB.Record.FaceRecordInfo>();
            record.RecordId = record.Id;
            record.Id = 0;
            record.DeviceSn = info.DeviceSn;
            record.PhotoData = info.File;
            _FaceRecordService.AddRecord(record);
            var comDel = await Allocator.GetCommandDetail(info.DeviceSn);
            var cmd = new PushCardRecord(comDel);
            Allocator.AddCommand(cmd);
        }
    }

    /// <summary>
    /// 后台服务器类扩展
    /// </summary>
    public static class MyBackgroundServiceExtend
    {
        /// <summary>
        /// 注册后台服务
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static IServiceCollection AddMyBackgroundService(this IServiceCollection services, IConfiguration configuration)
        {
            services.AddSingleton<IHostedService, MyBackgroundService>();
            Allocator.UDPIP = configuration["UdpIp"];
            Allocator.UDPPort = int.Parse(configuration["UdpPort"]);
            Allocator.DrivePort = int.Parse(configuration["DrivePort"]);
            MyBackgroundService.HostUrl = configuration["urls"];
            return services;
        }
    }
}
